(function () {
  let depCollectorId = 0;
  class DepCollector {
    constructor() {
      const id = ++depCollectorId;
      this.id = id;
      DepCollector.map[id] = this;
      this.list = [];
    }
    includes(watcher) {
      return this.list.includes(watcher);
    }
    push(watcher) {
      return this.list.push(watcher);
    }
    forEach(cb) {
      this.list.forEach(cb);
    }
    remove(watcher) {
      const index = this.list.indexOf(watcher);
      if (index !== -1) this.list.splice(index, 1);
    }
  }
  DepCollector.map = {};
  const KEY_DEP_ARRAY = Symbol('KEY_DEP_ARRAY'); // 数组作为整体触发
  const KEY_DEP = Symbol('KEY_DEP'); // 重用dep实例
  class Dep {
    constructor() {
      this.map = {};
    }
    add(key, watcher) {
      if (!watcher) return;
      if (!this.map[key]) this.map[key] = new DepCollector();
      watcher.addDepId(this.map[key].id);
      if (this.map[key].includes(watcher)) return;
      this.map[key].push(watcher);
    }
    notify(key) {
      if (!this.map[key]) return;
      this.map[key].forEach(watcher => watcher.queue());
    }
  }
  const proxy = function (target) {
    const dep = target[KEY_DEP] || new Dep();
    if (!target[KEY_DEP]) target[KEY_DEP] = dep;
    for (let key in target) {
      const child = target[key];
      if (child && typeof child === 'object') {
        target[key] = proxy(child); 
      }
    }
    return _proxyObj(target, dep, target instanceof Array);
  };
  const _proxyObj = function (target, dep, isArray) {
    const data = new Proxy(target, {
      get(target, key) {
        if (key !== KEY_DEP) dep.add(isArray ? KEY_DEP_ARRAY : key, currentWatcher);
        return target[key];
      },
      set(target, key, value) {
        if (key !== KEY_DEP) {
          if (value && typeof value === 'object') {
            value = proxy(value);
          }
          target[key] = value;
          dep.notify(isArray ? KEY_DEP_ARRAY : key);
          return true;
        }
      }
    });
    return data;
  };

  let watcherId = 0;
  class Watcher {
    constructor(func, cb) {
      this.id = ++watcherId;
      this.func = func;
      this.cb = cb;
    }

    eval() {
      this.depIds = this.newDepIds;
      this.newDepIds = {};
      pushWatcher(this);
      this.value = this.func(); // 缓存旧的值
      popWatcher();
      this.clearDeps();
    }

    addDepId(depId) {
      this.newDepIds[depId] = true;
    }

    clearDeps() { // 移除已经无用的依赖
      for (let depId in this.depIds) {
        if (!this.newDepIds[depId]) {
          DepCollector.map[depId].remove(this);
        }
      }
    }

    queue() {
      queueWatcher(this);
    }

    run() {
      const oldVal = this.value;
      this.eval(); // 重新计算并收集依赖
      this.cb(oldVal, this.value);
    }
  }
  let currentWatcheres = []; // 栈，computed属性
  let currentWatcher = null;
  const pushWatcher = function (watcher) {
    currentWatcheres.push(watcher);
    currentWatcher = watcher;
  };
  const popWatcher = function (watcher) {
    currentWatcheres.pop();
    currentWatcher = currentWatcheres.length > 0 ? currentWatcheres[currentWatcheres.length - 1] : null;
  };
  const watch = function (func, cb) {
    const watcher = new Watcher(func, cb);
    watcher.eval();
    return watcher;
  };

  window.proxy = proxy;
  window.watch = watch;
})();
